#include "stdafx.h"
#include "DarkSky.h"
#include "../main/Helper.h"
#include "../main/Logger.h"
#include "../httpclient/UrlEncode.h"
#include "hardwaretypes.h"
#include "../httpclient/HTTPClient.h"
#include "../main/json_helper.h"
#include "../main/RFXtrx.h"
#include "../main/mainworker.h"

#ifdef _DEBUG
//#define DEBUG_DarkSkyR
//#define DEBUG_DarkSkyW
#endif

#ifdef DEBUG_DarkSkyW
void SaveString2Disk(std::string str, std::string filename)
{
	FILE* fOut = fopen(filename.c_str(), "wb+");
	if (fOut)
	{
		fwrite(str.c_str(), 1, str.size(), fOut);
		fclose(fOut);
	}
}
#endif
#ifdef DEBUG_DarkSkyR
std::string ReadFile(std::string filename)
{
	std::ifstream file;
	std::string sResult = "";
	file.open(filename.c_str());
	if (!file.is_open())
		return "";
	std::string sLine;
	while (!file.eof())
	{
		getline(file, sLine);
		sResult += sLine;
	}
	file.close();
	return sResult;
}
#endif

CDarkSky::CDarkSky(const int ID, const std::string& APIKey, const std::string& Location) :
	m_APIKey(APIKey),
	m_Location(Location)
{
	m_HwdID = ID;
	Init();
}

void CDarkSky::Init()
{
}

bool CDarkSky::StartHardware()
{
	RequestStart();

	Init();
	//Start worker thread
	m_thread = std::make_shared<std::thread>([this] { Do_Work(); });
	SetThreadNameInt(m_thread->native_handle());
	m_bIsStarted = true;
	sOnConnected(this);
	return (m_thread != nullptr);
}

bool CDarkSky::StopHardware()
{
	if (m_thread)
	{
		RequestStop();
		m_thread->join();
		m_thread.reset();
	}
	m_bIsStarted = false;
	return true;
}

void CDarkSky::Do_Work()
{
	Log(LOG_STATUS, "Started...");

	int sec_counter = 290;
	while (!IsStopRequested(1000))
	{
		sec_counter++;
		if (sec_counter % 12 == 0) {
			m_LastHeartbeat = mytime(nullptr);
		}
		if (sec_counter % 300 == 0)
		{
			GetMeterDetails();
		}
	}
	Log(LOG_STATUS, "Worker stopped...");
}

bool CDarkSky::WriteToHardware(const char* /*pdata*/, const unsigned char /*length*/)
{
	return false;
}

std::string CDarkSky::GetForecastURL()
{
	std::stringstream sURL;
	std::string szLoc = CURLEncode::URLEncode(m_Location);
	sURL << "https://darksky.net/#/f/" << szLoc;
	return sURL.str();
}

void CDarkSky::GetMeterDetails()
{
	std::string sResult;
#ifdef DEBUG_DarkSkyR
	sResult = ReadFile("E:\\DarkSky.json");
#else
	std::stringstream sURL;
	std::string szLoc = m_Location;
	std::string szExclude = "minutely,hourly,daily,alerts,flags";
	sURL << "https://api.darksky.net/forecast/" << m_APIKey << "/" << szLoc << "?exclude=" << szExclude;
	try
	{
		if (!HTTPClient::GET(sURL.str(), sResult))
		{
			Log(LOG_ERROR, "Error getting http data!.");
			return;
		}
	}
	catch (...)
	{
		Log(LOG_ERROR, "Error getting http data!");
		return;
	}
#ifdef DEBUG_DarkSkyW
	SaveString2Disk(sResult, "E:\\DarkSky.json");
#endif

#endif
	Json::Value root;

	bool ret = ParseJSon(sResult, root);
	if ((!ret) || (!root.isObject()))
	{
		Log(LOG_ERROR, "Invalid data received! Check Location, use a City or GPS Coordinates (xx.yyyy,xx.yyyyy)");
		return;
	}
	if (root["currently"].empty() == true)
	{
		Log(LOG_ERROR, "Invalid data received, or unknown location!");
		return;
	}
	/*
	std::string tmpstr2 = root.toStyledString();
	FILE *fOut = fopen("E:\\DarkSky.json", "wb+");
	fwrite(tmpstr2.c_str(), 1, tmpstr2.size(), fOut);
	fclose(fOut);
	*/

	float temp;
	int humidity = 0;
	int barometric = 0;
	int barometric_forcast = baroForecastNoInfo;

	temp = root["currently"]["temperature"].asFloat();
	//Convert to celcius
	temp = float((temp - 32) * (5.0 / 9.0));

	if (root["currently"]["humidity"].empty() == false)
	{
		humidity = ground(root["currently"]["humidity"].asFloat() * 100.0F);
	}
	if (root["currently"]["pressure"].empty() == false)
	{
		barometric = atoi(root["currently"]["pressure"].asString().c_str());
		if (barometric < 1000)
			barometric_forcast = baroForecastRain;
		else if (barometric < 1020)
			barometric_forcast = baroForecastCloudy;
		else if (barometric < 1030)
			barometric_forcast = baroForecastPartlyCloudy;
		else
			barometric_forcast = baroForecastSunny;

		if (root["currently"]["icon"].empty() == false)
		{
			std::string forcasticon = root["currently"]["icon"].asString();
			if ((forcasticon == "partly-cloudy-day") || (forcasticon == "partly-cloudy-night"))
			{
				barometric_forcast = baroForecastPartlyCloudy;
			}
			else if (forcasticon == "cloudy")
			{
				barometric_forcast = baroForecastCloudy;
			}
			else if ((forcasticon == "clear-day") || (forcasticon == "clear-night"))
			{
				barometric_forcast = baroForecastSunny;
			}
			else if ((forcasticon == "rain") || (forcasticon == "snow"))
			{
				barometric_forcast = baroForecastRain;
			}
		}
	}

	if (barometric != 0)
	{
		//Add temp+hum+baro device
		SendTempHumBaroSensor(1, 255, temp, humidity, static_cast<float>(barometric), barometric_forcast, "THB");
	}
	else if (humidity != 0)
	{
		//add temp+hum device
		SendTempHumSensor(1, 255, temp, humidity, "TempHum");
	}
	else
	{
		//add temp device
		SendTempSensor(1, 255, temp, "Temperature");
	}

	//Wind
	int wind_degrees = -1;
	float windspeed_ms = 0;
	float windgust_ms = 0;
	float wind_temp = temp;
	float wind_chill = temp;
	//int windgust=1;
	//float windchill=-1;

	if (root["currently"]["windBearing"].empty() == false)
	{
		wind_degrees = atoi(root["currently"]["windBearing"].asString().c_str());
	}
	if (root["currently"]["windSpeed"].empty() == false)
	{
		if ((root["currently"]["windSpeed"] != "N/A") && (root["currently"]["windSpeed"] != "--"))
		{
			float temp_wind_mph = static_cast<float>(atof(root["currently"]["windSpeed"].asString().c_str()));
			if (temp_wind_mph != -9999.00F)
			{
				//convert to m/s
				windspeed_ms = temp_wind_mph * 0.44704F;
			}
		}
	}
	if (root["currently"]["windGust"].empty() == false)
	{
		if ((root["currently"]["windGust"] != "N/A") && (root["currently"]["windGust"] != "--"))
		{
			float temp_wind_gust_mph = static_cast<float>(atof(root["currently"]["windGust"].asString().c_str()));
			if (temp_wind_gust_mph != -9999.00F)
			{
				//convert to m/s
				windgust_ms = temp_wind_gust_mph * 0.44704F;
			}
		}
	}
	if (root["currently"]["apparentTemperature"].empty() == false)
	{
		if ((root["currently"]["apparentTemperature"] != "N/A") && (root["currently"]["apparentTemperature"] != "--"))
		{
			wind_chill = static_cast<float>(atof(root["currently"]["apparentTemperature"].asString().c_str()));
			//Convert to celcius
			wind_chill = float((wind_chill - 32) * (5.0 / 9.0));
		}
	}
	if (wind_degrees != -1)
	{
		RBUF tsen;
		memset(&tsen, 0, sizeof(RBUF));
		tsen.WIND.packetlength = sizeof(tsen.WIND) - 1;
		tsen.WIND.packettype = pTypeWIND;
		tsen.WIND.subtype = sTypeWIND4;
		tsen.WIND.battery_level = 9;
		tsen.WIND.rssi = 12;
		tsen.WIND.id1 = 0;
		tsen.WIND.id2 = 1;

		float winddir = float(wind_degrees);
		int aw = ground(winddir);
		tsen.WIND.directionh = (BYTE)(aw / 256);
		aw -= (tsen.WIND.directionh * 256);
		tsen.WIND.directionl = (BYTE)(aw);

		tsen.WIND.av_speedh = 0;
		tsen.WIND.av_speedl = 0;
		int sw = ground(windspeed_ms * 10.0F);
		tsen.WIND.av_speedh = (BYTE)(sw / 256);
		sw -= (tsen.WIND.av_speedh * 256);
		tsen.WIND.av_speedl = (BYTE)(sw);

		tsen.WIND.gusth = 0;
		tsen.WIND.gustl = 0;
		int gw = ground(windgust_ms * 10.0F);
		tsen.WIND.gusth = (BYTE)(gw / 256);
		gw -= (tsen.WIND.gusth * 256);
		tsen.WIND.gustl = (BYTE)(gw);

		//this is not correct, why no wind temperature? and only chill?
		tsen.WIND.chillh = 0;
		tsen.WIND.chilll = 0;
		tsen.WIND.temperatureh = 0;
		tsen.WIND.temperaturel = 0;

		tsen.WIND.tempsign = (wind_temp >= 0) ? 0 : 1;
		int at10 = ground(std::abs(wind_temp * 10.0F));
		tsen.WIND.temperatureh = (BYTE)(at10 / 256);
		at10 -= (tsen.WIND.temperatureh * 256);
		tsen.WIND.temperaturel = (BYTE)(at10);

		tsen.WIND.chillsign = (wind_chill >= 0) ? 0 : 1;
		at10 = ground(std::abs(wind_chill * 10.0F));
		tsen.WIND.chillh = (BYTE)(at10 / 256);
		at10 -= (tsen.WIND.chillh * 256);
		tsen.WIND.chilll = (BYTE)(at10);

		sDecodeRXMessage(this, (const unsigned char*)&tsen.WIND, nullptr, 255, nullptr);
	}

	//UV
	if (root["currently"]["uvIndex"].empty() == false)
	{
		if ((root["currently"]["uvIndex"] != "N/A") && (root["currently"]["uvIndex"] != "--"))
		{
			float UV = root["currently"]["uvIndex"].asFloat();
			if ((UV < 16) && (UV >= 0))
			{
				SendUVSensor(0, 1, 255, UV, "UV Index");
			}
		}
	}

	//Rain
	if (root["currently"]["precipIntensity"].empty() == false)
	{
		if ((root["currently"]["precipIntensity"] != "N/A") && (root["currently"]["precipIntensity"] != "--"))
		{
			float rainrateph = static_cast<float>(atof(root["currently"]["precipIntensity"].asString().c_str())) * 25.4F; // inches to mm
			if ((rainrateph != -9999.00F) && (rainrateph >= 0.00F))
			{
				SendRainRateSensor(1, 255, rainrateph, "Rain");
			}
		}
	}

	//Visibility
	if (root["currently"]["visibility"].empty() == false)
	{
		if ((root["currently"]["visibility"] != "N/A") && (root["currently"]["visibility"] != "--"))
		{
			float visibility = static_cast<float>(atof(root["currently"]["visibility"].asString().c_str())) * 1.60934F; // miles to km
			if (visibility >= 0)
			{
				_tGeneralDevice gdevice;
				gdevice.subtype = sTypeVisibility;
				gdevice.floatval1 = visibility;
				sDecodeRXMessage(this, (const unsigned char*)&gdevice, nullptr, 255, nullptr);
			}
		}
	}
	//Solar Radiation
	if (root["currently"]["ozone"].empty() == false)
	{
		if ((root["currently"]["ozone"] != "N/A") && (root["currently"]["ozone"] != "--"))
		{
			float radiation = static_cast<float>(atof(root["currently"]["ozone"].asString().c_str()));
			if (radiation >= 0.0F)
			{
				SendCustomSensor(1, 0, 255, radiation, "Ozone Sensor", "DU"); //(dobson units)
			}
		}
	}
	//Cloud Cover
	if (root["currently"]["cloudCover"].empty() == false)
	{
		if ((root["currently"]["cloudCover"] != "N/A") && (root["currently"]["cloudCover"] != "--"))
		{
			float cloudcover = static_cast<float>(atof(root["currently"]["cloudCover"].asString().c_str()));
			if (cloudcover >= 0.0F)
			{
				SendPercentageSensor(1, 0, 255, cloudcover * 100.0F, "Cloud Cover");
			}
		}
	}

}

